Skip to content

refactor(types): retire the five forks that shadowed a @objectstack/spec vocabulary (#2944) - #2985

Merged
os-zhuang merged 1 commit into
mainfrom
refactor/2944-retire-spec-forks
Jul 30, 2026
Merged

refactor(types): retire the five forks that shadowed a @objectstack/spec vocabulary (#2944)#2985
os-zhuang merged 1 commit into
mainfrom
refactor/2944-retire-spec-forks

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #2944.

Five declarations in @object-ui/types restated a spec vocabulary, four of them re-exported under the spec's own symbol name — so an importer of @object-ui/types could not tell which definition they had. Every one had already drifted.

Declaration Was Spec Missing
ChartTypeSchema (zod/data-display.zod.ts:209) 7 19 12 values
ChartType (data-display.ts:689) 7 19 12 values
PageTypeSchema (zod/layout.zod.ts:258) 4 5 list
PageType (layout.ts:432) 10 5 + local added 5 the spec repudiates
ReportType (reports.ts:337) 3 4 joined
ActionType (ui-action.ts:72) 5 6 form

Why this one mattered more than it looks

It is why #2901 was filed with an inverted premise. That issue read the 7-value ChartTypeSchema as the protocol and concluded plugin-charts had outgrown it with 13 renderer-local names. The spec has 19 — the 7-value list was this fork, wearing the spec's name.

ActionType is the same failure in miniature: its union carried a doc comment asserting it was "the canonical definition from @objectstack/spec" while missing form, which ActionRunner.executeForm implements. A host app typing against @object-ui/types got a type error on code that works.

The general shape, worth naming: the recurring failure is not "renderer forgot a name", it is "a second definition exists and the renderer is faithful to the wrong one."

Widening only

Nothing was removed, so no existing value stops type-checking or validating. ActionType gains form, ReportType gains joined, ChartType goes 7 → 19, PageTypeSchema gains list.

The ChartType widening was the one real risk — an exhaustive switch would stop being exhaustive. Checked: 76/76 type-check tasks pass, so there is none.

PageType is not mechanical, and is handled differently

Narrowing it to the spec's 5 would drop grid/gallery/kanban/calendar/timeline — which ui/page.zod.ts explicitly says are visualizations rather than page kinds, but which a p1-spec-alignment test asserts and which external code may assign.

So the spec-owned half is now derived, and the objectui-only half is PageVisualizationAlias — a named, documented, sanctioned local extension, which is what #2231 actually prescribes for genuine renderer-only concerns. The union is unchanged in membership; the difference is that the spec half can no longer drift and the local half is no longer hiding. Removing it is the separate "visualizations are not page types" cleanup.

Guards

  • spec-subschema-parity.test.ts now pins both zod schemas by referencetoBe, so a faithful copy fails too, because a copy is a fork.
  • New spec-derived-unions.test.ts covers the three type aliases, which reference identity cannot reach (types erase at runtime, and a restated union that happens to match is indistinguishable from a derived one). It uses satisfies for compile-time coverage plus runtime assertions that the spec enum still contains the specific members each fork had dropped.

Verification

Repo type-check 76/76 tasks
Full test suite 8215 pass, 24 skipped, across 700 files
packages/types tsc 0 errors
eslint 0 errors

🤖 Generated with Claude Code

…pec vocabulary (#2944)

Five declarations in `@object-ui/types` restated a spec vocabulary, four of them
re-exported under **the spec's own symbol name** — so an importer could not tell
which definition they had. Every one had already drifted:

  ChartTypeSchema   7  → spec 19
  ChartType         7  → spec 19
  PageTypeSchema    4  → spec 5   (missing `list`)
  PageType         10  → spec 5   (five the spec explicitly repudiates)
  ReportType        3  → spec 4   (missing `joined`)
  ActionType        5  → spec 6   (missing `form`)

All now come from the spec: the zod schemas by reference, the type aliases
re-exported or derived via `z.infer`.

This is why #2901 was filed with an inverted premise — it read the 7-value
`ChartTypeSchema` as the protocol and concluded `plugin-charts` had outgrown it
with renderer-local dialect. The spec has 19; the 7-value list was this fork.
`ActionType`'s union carried a doc comment claiming to be "the canonical
definition from @objectstack/spec" while missing `form`, which
`ActionRunner.executeForm` implements — so a host app got a type error on code
that works.

Widening only: nothing was removed, so no existing value stops type-checking or
validating.

`PageType` keeps its five visualization names as `PageVisualizationAlias`, a
named and documented local extension rather than names hidden inside a
hand-written union. They are not page kinds (`ui/page.zod.ts` says so), but
narrowing them away breaks anyone assigning `pageType: 'kanban'` — that is the
separate cleanup the old comment referred to.

Guards: `spec-subschema-parity.test.ts` pins both zod schemas by reference (a
faithful copy fails — a copy is a fork); the new `spec-derived-unions.test.ts`
covers the type aliases, which reference identity cannot reach, via `satisfies`
plus runtime vocabulary assertions.

Verified: 76/76 type-check tasks, 8215 tests across 700 files, eslint 0 errors.

Closes #2944

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Jul 30, 2026 7:02am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 27.9 KB 350 KB
Entry file index-CDVmrVQB.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.20KB 2.97KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.25KB 1.01KB
auth (org-roles.js) 6.72KB 2.85KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 450.74KB 98.10KB
core (index.js) 2.16KB 0.78KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 134.67KB 34.24KB
fields (index.js) 221.10KB 54.18KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 25.17KB 5.80KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 38.45KB 10.67KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 4.42KB 1.27KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 1.77KB 0.77KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 6.84KB 2.42KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.90KB 12.35KB
plugin-charts (index.js) 57.26KB 16.24KB
plugin-chatbot (index.js) 179.93KB 42.67KB
plugin-dashboard (index.js) 109.60KB 28.33KB
plugin-designer (index.js) 210.56KB 42.56KB
plugin-detail (index.js) 216.52KB 53.02KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 103.32KB 25.08KB
plugin-gantt (index.js) 162.26KB 39.53KB
plugin-grid (index.js) 179.45KB 47.03KB
plugin-kanban (index.js) 47.82KB 13.18KB
plugin-list (index.js) 98.30KB 23.23KB
plugin-map (index.js) 16.80KB 5.24KB
plugin-markdown (index.js) 13.65KB 4.67KB
plugin-report (index.js) 37.77KB 10.00KB
plugin-timeline (index.js) 25.03KB 7.11KB
plugin-tree (index.js) 8.36KB 2.81KB
plugin-view (index.js) 85.47KB 20.82KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.55KB 0.67KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 4.80KB 2.03KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 18.70KB 6.09KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 3.47KB 1.54KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 0.77KB 0.41KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 1.92KB 0.93KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.04KB 1.93KB
types (system-fields.js) 2.39KB 1.17KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 0.75KB 0.46KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang merged commit ea7f477 into main Jul 30, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the refactor/2944-retire-spec-forks branch July 30, 2026 07:08
xuyushun441-sys pushed a commit that referenced this pull request Jul 30, 2026
Sync before merging so CI validates the real merge result — main picked up a
@object-ui/types refactor (#2985) and two plugin-form wizard changes while this
branch was in flight.
os-zhuang added a commit that referenced this pull request Jul 30, 2026
…aring one navigator (#2944) (#2994)

The last open item of #2944. `ActionRunner` dispatched a seventh action type,
`navigation`, that `@objectstack/spec`'s `ActionType` does not contain. The issue
asked to promote it upstream or delete the case. Neither, as stated.

Promoting is wrong: the spec already names this operation — `type: 'url'`, with
`openIn` for the new-tab choice — so a seventh type would put a second spec name
on one operation, which is the failure this audit exists to describe, committed
one level up. Deleting is worse because it is silent: `{ type: 'navigation',
to: … }` is authored today (`element:button` CTAs), and without the case the
action falls through to `executeActionSchema`, which returns `{ success: true }`
— a green toast that navigates nowhere (#2960's trap).

The alias had already drifted, which is the actual finding. `executeNavigation`
was quietly the weaker of two implementations of one concept: no `${param.X}`
interpolation, `openIn` ignored, no `/api/…` full-page short-circuit. So
`{ type: 'navigation', to: '/x?p=${param.p}' }` shipped the literal
`${param.p}` while the identical `url` action resolved it.

So it stays and stops being dialect. `ObjectUiLocalActionType` /
`OBJECTUI_LOCAL_ACTION_TYPES` declare it as objectui's own alias of `url` — the
treatment #2985 gave the page-visualization names — and both names now route
through one `navigateTo`. `url` gains `replace` pass-through, the one modifier
only the alias carried; `replace` is omitted from the `NavigationHandler`
options when unset, so hosts see the shape they already saw.

The guard is structural rather than another assertion: built-in dispatch is a
table typed `Record<RunnableActionType, …>` instead of a `switch`, so an
`ActionType` the spec ADDS stops compiling until an executor exists for it —
#2942's "validates at save, renders nothing at run time" class, made a build
error for actions. `spec-derived-unions.test.ts` additionally asserts
`navigation` is absent from the spec enum, so the day it is adopted upstream the
test fails and names the alias to retire.

Also covers the `/api/` short-circuit and `apiBase` promotion, which had none —
the project runs in a node env with no `window`, which is why — and corrects the
audit doc's Direction B row, which still prescribed promoting.

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retire the five objectui forks that shadow a @objectstack/spec symbol name (#2901)

1 participant